#!/usr/bin/env Rscript

### Toggle debug blocks on and off in a fashion of a pre-processor
#   usage: toggle_debug [uri] [on|off]
#
#   Argument uri can be a single file or directory.
#   In case of a directory, the script processes all ".[rR]$" files inside and
#   only writes changes if processing is successful for all files found.
#
#   Debug blocks start with the comment "BDEBUG" and end with "EDEBUG"
#   Toggled off code is preceded with "#DEBUG# ".
#
#   Note that this script does not work nice with tabs.
#   If any tabs are found, they are converted to option [tab.width] spaces.
#   Setting your editor to spaces is highly recommended.

### Options
# FIXME: maybe allow more options like comment.str
tab.width = as.integer(2)



tabs2spaces = function(x) {
  gsub("\t", collapse(rep(" ", tab.width), ""), x)
}

getIndentInfo = function(x) {
  indent = tabs2spaces(sub("^([[:blank:]]*).*", "\\1", x))
  list(string = indent, count = nchar(indent))
}

addComment = function(x, indent, comment.str = "#DEBUG# ") {
  paste0(indent$string, comment.str, x)
}

rmComment = function(x, comment.str = "#DEBUG# ") {
  sub(paste0("^[[:space:]]*", comment.str, "(.*)"), "\\1", x)
}

is.commented = function(x, comment.str = "#DEBUG# ") {
  grepl(comment.str, x, fixed = TRUE)
}


toggle_debug = function(fn, action = "on", dry = FALSE) {
  src = readLines(fn)
  fname = basename(fn)
  in.debug = FALSE
  last.block = 0L

  for (ln in seq_along(src)) {
    line = src[ln]

    if (grepl("^[[:blank:]]*#+[[:blank:]]*[BE]DEBUG[[:space:]]*$", line)) {
      if (grepl("BDEBUG", line, fixed = TRUE)) {
        if (in.debug)
          stopf("Error in '%s' (%i): Tried to open new debug block before closing previous block starting in line %i",
                fname, ln, last.block)
        in.debug = TRUE
        last.block = ln
        indent = getIndentInfo(line)
      } else {
        if (!in.debug)
          stopf("Error in '%s' (%i): Tried to close debug block before opening it",
                fname, ln, last.block)
        in.debug = FALSE
      }
    } else if (in.debug) {
      if (action == "on") {
        if (is.commented(line))
          stopf("Parts of file '%s' are already commented out, e.g. line %i", fname, ln)
        src[ln] = addComment(line, indent)
      } else {
        if (!is.commented(line))
          stopf("Parts of file '%s' are not commented out, e.g. line %i", fname, ln)
        src[ln] = rmComment(line)
      }
    }
  }

  if (in.debug)
    stopf("Error in '%s': EOF reached, debug block starting in line %i not closed",
                 fname, last.block)

  if (last.block > 0L && !dry)
    writeLines(src, con = fn)

  invisible(list(src = src, touched = last.block > 0L))
}


library(BBmisc)

argv = commandArgs(trailingOnly = TRUE)
if (length(argv) != 2L || !(argv[2L] %in% c("on", "off")))
  stop("Usage: toggle_debug [uri] [on|off]")
uri = argv[1L]
action = argv[2L]

if (! file.exists(uri))
  stopf("Could not open '%s'", uri)

if (file_test("-d", uri)) {
  files = list.files(uri, pattern = "\\.R$", ignore.case = TRUE, full.names = TRUE)
  src = lapply(files, toggle_debug, action = action, dry = TRUE)
  ind = extractSubList(src, "touched")
  res = mapply(writeLines,
               text = extractSubList(src[ind], "src", simplify = FALSE),
               con = files[ind])
} else {
  toggle_debug(uri, action)
}

invisible(0L)
